#include "BPlusTree.h"
#include "hilbert.h"
#include "Record.h"
#include <iostream>
#include <stdlib.h>

//Prototype declarations
void Pad (char *inString);
void ConvertToString (char *output, int xCoord, int yCoord);
void ConvertToCoords (char *input, int &xCoord, int &yCoord);
int GetBounds (char *lowPoint, char *highPoint, int xlow, int ylow, int xhigh, int yhigh);

int main () {

	std::cout << "What do you want to do?\n";
	std::cout << "1. Load a text file into a binary database file.\n";
	std::cout << "2. Apply a selection predicate to a binary database file.\n";
	std::cout << "3. Find all records falling in a range using a B+-Tree.\n";
	std::cout << "4. Find all records falling in a range using a spatial index.\n>";

	int choice;
	std::cin >> choice;
	//choice = 1;
	//choice = 3;
	
	if (choice == 1)
	{
		std::cout << "What do you want to do?\n";
		std::cout << "1. Create a binary file without an index.\n";
		std::cout << "2. Create a binary file with a B+-Tree index.\n";
		std::cout << "3. Create a binary file with a spatial index.\n>";
		std::cin >> choice;
		//choice = 2;
		
		if(choice == 1)
		{
			std::cout << "What is the name of the text file to load?\n>";
			char ans1[100];
			std::cin >> ans1;

			std::cout << "What is the name of the file where the schema is located?\n>";
			char ans2[100];
			std::cin >> ans2;

			std::cout << "What is the name of the relation schema?\n>";
			char ans3[100];
			std::cin >> ans3;

			std::cout << "Where should the binary file be stored?\n>";
			char ans4[100];
			std::cin >> ans4;

			// load in the relation's schema
			Schema mySchema (ans2, ans3);

			// open up the file
			File dbFile;
			dbFile.Open (0, ans4);

			// this will store the data
			Record temp;

			// this is the text file to process
			FILE *inFile = fopen (ans1, "r");

			// load it up
			int i;
			while (temp.SuckNextRecord (mySchema, inFile)) {
				if (i % 1000 == 0)
					std::cerr << ".";
				dbFile.Append (temp);
				++i;
			}
			std::cerr << "Done!\n";

			// and close it
			dbFile.Close ();
		}
		else if(choice ==2)
		{
			std::cout << "What is the name of the text file to load?\n>";
			char text_file[100];
			std::cin >> text_file;
			//strcpy(text_file,"partsupp.tbl");
						
			std::cout << "What is the name of the file where the schema is located?\n>";
			char schema_loc[100];
			std::cin >> schema_loc;
			//strcpy(schema_loc,"relschema");
			
			std::cout << "What is the name of the relation schema?\n>";
			char rel_schema[100];
			std::cin >> rel_schema;
			//strcpy(rel_schema,"partsupp");
			
			std::cout << "Where should the binary file be stored?\n>";
			char bin_file[100];
			std::cin >> bin_file;
			//strcpy(bin_file,"partsuppFile");
			
			std::cout << "What attribute would you like to index?\n>";
			char attribute1[100];
			std::cin >> attribute1;
			//strcpy(attribute1, "ps_suppkey");
			
			std::cout << "Where should the B+-Tree index file be stored?\n>";
			char index_loc[100];
			std::cin >> index_loc;
			//strcpy(index_loc, "partsupp.bt");
			
			// load in the relation's schema
			Schema mySchema (schema_loc, rel_schema);
			
			// open up the file
			File dbFile;
			dbFile.Open (0, bin_file);

			BPlusTree myTree;
			myTree.Create (index_loc, Int);
			
			// this will store the data
			Record data_rec;
			
			// this is the text file to process
			FILE *inFile = fopen (text_file, "r");
			
			int key_index = mySchema.Find(attribute1);
			Type key_type = mySchema.FindType ( key_index );
			
			// load it up
			int i = 0;
			while (data_rec.SuckNextRecord (mySchema, inFile)) {
				if (i % 1000 == 0){
					std::cerr << ".";
				}
				
				//data_rec.Print(mySchema);
				switch(key_type)
				{
					case Int:
					{
						char key[sizeof(int)];
						*((int *)key) = data_rec.GetInt (key_index);
						
						char pg_num[sizeof(int)];
						*((int *) pg_num) = dbFile.Append (data_rec);
						
						myTree.Insert (key, *((int *) pg_num));
						break;
					}
					case Double:
					{
						char key[sizeof(double)];
						*((double *)key) = data_rec.GetDouble (key_index);
						
						char pg_num[sizeof(int)];
						*((int *) pg_num) = dbFile.Append (data_rec);
						
						myTree.Insert (key, *((int *) pg_num));
						break;
					}
					default://default is string
					{
						char *key = data_rec.GetString (key_index);
						
						char pg_num[sizeof(int)];
						*((int *) pg_num) = dbFile.Append (data_rec);
						
						myTree.Insert (key, *((int *) pg_num));
						delete [] key;
						break;
					}
				}
					
				++i;
			}
			std::cerr << "Done!\n";

			// and close it
			dbFile.Close ();
			myTree.Close ();
		}
		else if (choice == 3)
		{
			std::cout << "What is the name of the text file to load?\n>";
			char text_file[100];
			std::cin >> text_file;
			//strcpy(text_file,"points.tbl");
			
			std::cout << "What is the name of the file where the schema is located?\n>";
			char schema_loc[100];
			std::cin >> schema_loc;
			//strcpy(schema_loc,"relschema");
			
			std::cout << "What is the name of the relation schema?\n>";
			char rel_schema[100];
			std::cin >> rel_schema;
			//strcpy(rel_schema,"points");
			
			std::cout << "Where should the binary file be stored?\n>";
			char bin_file[100];
			std::cin >> bin_file;
			//strcpy(bin_file,"pointsFile");
			
			std::cout << "What two attributes would you like to index?\nAttribute1>";
			char attribute1[100];
			std::cin >> attribute1;
			//strcpy(attribute1,"x");
			std::cout << "Attribute2>";
			char attribute2[100];
			std::cin >> attribute2;
			//strcpy(attribute2,"y");

			std::cout << "Where should the index file be stored?\n>";
			char index_loc[100];
			std::cin >> index_loc;
			//strcpy(index_loc,"points.bt");

			// load in the relation's schema
			Schema mySchema (schema_loc, rel_schema);

			// open up the file
			File dbFile;
			dbFile.Open (0, bin_file);

			BPlusTree myTree;
			myTree.Create (index_loc, String);

			// this will store the data
			Record data_rec;

			// this is the text file to process
			FILE *inFile = fopen (text_file, "r");

			int key_index1 = mySchema.Find(attribute1);
			Type key_type1 = mySchema.FindType ( key_index1 );
			
			int key_index2 = mySchema.Find(attribute2);
			Type key_type2 = mySchema.FindType ( key_index2 );
			
			// load it up
			int i = 0;
			while (data_rec.SuckNextRecord (mySchema, inFile)) {
			
				char xval[sizeof(int)], yval[sizeof(int)];
				*((int *) xval) = data_rec.GetInt (key_index1);
				*((int *) yval) = data_rec.GetInt (key_index2);

				char key[100];
				ConvertToString (key, *((int *) xval), *((int *) yval));

				char pg_num[sizeof(int)];
				*((int *) pg_num) = dbFile.Append (data_rec);

				myTree.Insert (key, *((int *) pg_num));
				
				//std::cerr << "\nInserted x: " << *((int *) xval) << " y: " << *((int *) yval) << " at page: " << *((int *) pg_num) << " With key: " << key;
				
				//delete [] key;
				if (i % 1000 == 0){
					std::cerr << ".";
				}
				++i;
			}
			
			myTree.Close();
			dbFile.Close();
		}

	}
	else if (choice == 2)
	{
	}
	else if (choice == 3)
	{
		std::cout << "What is the name of the file where the schema is located?\n>";
		char schema_loc[100];
		std::cin >> schema_loc;
		//strcpy(schema_loc, "relschema");
		
		std::cout << "What is the name of the relation schema?\n>";
		char rel_schema[100];
		std::cin >> rel_schema;
		//strcpy(rel_schema, "partsupp");
		
		std::cout << "What is the name of the binary data file?\n>";
		char bin_file[100];
		std::cin >> bin_file;
		//strcpy(bin_file,"partsuppFile");
		
		std::cout << "What is the name of the attribute to be queried over?\n>";
		char attribute1[100];
		std::cin >> attribute1;
		//strcpy(attribute1,"ps_suppkey");
		
		std::cout << "What is the lower bound of the query range?\n>";
		char l_bound[sizeof(int)];
		int temp_int1;
		std::cin >> temp_int1;
		//sprintf(l_bound,"%d",4000);
		//*((int *) l_bound) = 4000;
		*((int *) l_bound) = temp_int1;
		
		
		std::cout << "What is the upper bound of the query range?\n>";
		char u_bound[sizeof(int)];
		int temp_int2;
		//int u_bound = 4002;
		//std::cin >> u_bound;
		std::cin >> temp_int2;
		//sprintf(u_bound,"%d",4002);
		//*((int *) u_bound) = 4002;
		*((int *) u_bound) = temp_int2;
		
		std::cout << "What is the name of the B+-Tree file that will be used to answer the query?\n>";
		char bplus_file[100];
		std::cin >> bplus_file;
		//strcpy(bplus_file,"partsupp.bt");
		
		
		//binary data file
		File dbFile;
		dbFile.Open (1, bin_file);
		
		//load in schema
		Schema mySchema (schema_loc, rel_schema);
		
		int key_index = mySchema.Find(attribute1);
		
		Type key_type = mySchema.FindType ( key_index );
		switch(key_type)
		{
			case Int:
			{
				BPlusTree myTree;
				myTree.Open (bplus_file);
				
				// If the get page has a value greater than the high dont do next
				bool greater_than_high = false;
				bool correct = false;
				
				int numReturned;
				
				IndexRecord *result = myTree.GetPage (l_bound, numReturned);
				//delete [] bound;
				for(int i = 0; i < numReturned; ++i)
				{
					correct = false;

					// If record lies within bound
					if(result[i].Compare (l_bound, Int) >= 0){
						if(result[i].Compare (u_bound, Int) > 0){
							greater_than_high = true;
						}
						else{
							correct = true;
						}
					}

					if( correct ){
						int page_pointer = result[i].GetPointer();
						Page page;
						dbFile.GetPage(page, page_pointer);
						
						Record r;
						//char val[sizeof(int)];
						int val;
						while(page.GetFirst (r)){
							
							val = r.GetInt(key_index);
							
							if(val >= *((int *) l_bound)){
								if(val > *((int *) u_bound)){
									greater_than_high = true;
								}
								else{
									r.Print(mySchema);
								}
							}
						}
					}
						
					if (i % 100 == 0){
						std::cerr << ".";
					}
				}
				delete [] result;
				
				while (!greater_than_high) 
				{
					result = myTree.GetNext (numReturned);
					if (result == 0)
						break;
					for (int i = 0; i < numReturned; i++) {
						correct = false;
						
						if(result[i].Compare (l_bound, Int) >= 0){
							if(result[i].Compare (u_bound, Int) > 0){
								greater_than_high = true;
							}
							else{
								correct = true;
							}
						}
						if( correct ){
							int page_pointer = result[i].GetPointer();
							Page page;
							dbFile.GetPage(page, page_pointer);
							
							Record r;
							//char val[sizeof(int)];
							int val;
							while(page.GetFirst (r)){

								val = r.GetInt(key_index);

								if(val >= *((int *) l_bound)){
									if(val > *((int *) u_bound)){
										greater_than_high = true;
									}
									else{
										r.Print(mySchema);
									}
								}

							}
						}
						if (i % 100 == 0){
							std::cerr << ".";
						}
					}
					delete [] result;
				}
				myTree.Close();
				break;
			}
			case Double:
			{
				break;
			}
			case String:
			{
				break;
			}
			default:
				std::cout << "Bad Type Chosen\n";
				exit(1);
		}
		std::cout << "Finished!\n";
		dbFile.Close();
	}
	else if(choice == 4)
	{
		std::cout << "What is the name of the file where the schema is located?\n>";
		char schema_loc[100];
		std::cin >> schema_loc;
		//strcpy(schema_loc, "relschema");
		
		std::cout << "What is the name of the relation schema?\n>";
		char rel_schema[100];
		std::cin >> rel_schema;
		//strcpy(rel_schema, "points");
		
		std::cout << "What is the name of the binary data file?\n>";
		char bin_file[100];
		std::cin >> bin_file;
		//strcpy(bin_file,"pointsFile");
		
		std::cout << "What are the names of the two attributes to be queried over?\nAttribute1>";
		char attribute1[100];
		std::cin >> attribute1;
		//strcpy(attribute1,"x");
		std::cout << "Attribute2>";
		char attribute2[100];
		std::cin >> attribute2;
		//strcpy(attribute2,"y");
		
		std::cout << "What is the lower bound (firstatt, secatt) of the query box?\nfirstatt>";
		char l_firstatt[100];
		int l_firstatt_temp;
		std::cin >> l_firstatt_temp;
		*((int *) l_firstatt) = l_firstatt_temp;
		std::cout << "secondatt>";
		char l_secatt[100];
		int l_secatt_temp;
		std::cin >> l_secatt_temp;
		*((int *) l_secatt) = l_secatt_temp;
		
		std::cout << "What is the upper bound (firstatt, secatt) of the query box?\nfirstatt>";
		char u_firstatt[100];
		int u_firstatt_temp;
		std::cin >> u_firstatt_temp;
		*((int *) u_firstatt) = u_firstatt_temp;
		std::cout << "second att>";
		char u_secatt[100];
		int u_secatt_temp;
		std::cin >> u_secatt_temp;
		*((int *) u_secatt) = u_secatt_temp;
		
		std::cout << "What is the name of the spatial index file that will be used to answer the query?\n>";
		char index_loc[100];
		std::cin >> index_loc;
		//strcpy(index_loc,"points.bt");
		
		
		// Get the Bounds
		char lowerBound[100];
		char upperBound[100];
		GetBounds (lowerBound, upperBound, *((int *) l_firstatt), *((int *) l_secatt), *((int *) u_firstatt), *((int *) u_secatt));
		
		//binary data file
		File dbFile;
		dbFile.Open (1, bin_file);
		
		//load in schema
		Schema mySchema (schema_loc, rel_schema);
		int key_index1 = mySchema.Find(attribute1);
		Type key_type1 = mySchema.FindType ( key_index1 );
		int key_index2 = mySchema.Find(attribute2);
		Type key_type2 = mySchema.FindType ( key_index2 );
		
		
		BPlusTree myTree;
		myTree.Open (index_loc);
		
		// If the get page has a value greater than the high dont do next
		bool greater_than_high = false;
		bool correct = false;

		int k_xCoord, k_yCoord;

		int numReturned;
		IndexRecord *result = myTree.GetPage (lowerBound, numReturned);
		//delete [] bound;
		for(int i = 0; i < numReturned; ++i)
		{
			correct = false;
			
			char *key = result[i].GetString(0);
			// If record lies within bound
			ConvertToCoords (key, k_xCoord, k_yCoord);
			if( (k_xCoord >= *((int *) l_firstatt)) && (k_yCoord >= *((int *) l_secatt)) ){
				if( (k_xCoord <= *((int *) u_firstatt)) && (k_yCoord <= *((int *) u_secatt)) ){
					correct = true;
				}
				else if( (k_xCoord > *((int *) u_firstatt)) && (k_yCoord > *((int *) u_secatt)) ){
					greater_than_high = true;
				}
			}
			
			if( correct ){
				int page_pointer = result[i].GetPointer();
				Page page;
				dbFile.GetPage(page, page_pointer);
				
				Record r;
				int x,y;
				while(page.GetFirst (r)){
					
					x = r.GetInt(key_index1);
					y = r.GetInt(key_index2);

					if( (x >= *((int *) l_firstatt)) && (y >= *((int *) l_secatt)) ){
						if( (x <= *((int *) u_firstatt)) && (y <= *((int *) u_secatt)) ){
							r.Print(mySchema);
						}
						else if( (x > *((int *) u_firstatt)) && (y > *((int *) u_secatt)) ){
							greater_than_high = true;
						}
					}

				}
			}
			delete [] key;
		}
		delete [] result;

		while (!greater_than_high) 
		{
			result = myTree.GetNext (numReturned);
			if (result == 0)
				break;
			for (int i = 0; i < numReturned; i++) {
				correct = false;
				
				char *key = result[i].GetString(0);
				// If record lies within bound
				ConvertToCoords (key, k_xCoord, k_yCoord);
				if( (k_xCoord >= *((int *) l_firstatt)) && (k_yCoord >= *((int *) l_secatt)) ){
					if( (k_xCoord <= *((int *) u_firstatt)) && (k_yCoord <= *((int *) u_secatt)) ){
						correct = true;
					}
					else if( (k_xCoord > *((int *) u_firstatt)) && (k_yCoord > *((int *) u_secatt)) ){
						greater_than_high = true;
					}
				}
				
				if( correct ){
					int page_pointer = result[i].GetPointer();
					Page page;
					dbFile.GetPage(page, page_pointer);
					
					Record r;
					//char val[sizeof(int)];
					int x,y;
					while(page.GetFirst (r)){

						x = r.GetInt(key_index1);
						y = r.GetInt(key_index2);

						if( (x >= *((int *) l_firstatt)) && (y >= *((int *) l_secatt)) ){
							if( (x <= *((int *) u_firstatt)) && (y <= *((int *) u_secatt)) ){
								r.Print(mySchema);
							}
							else if( (x > *((int *) u_firstatt)) && (y > *((int *) u_secatt)) ){
								greater_than_high = true;
							}
						}
					}
				}
				delete [] key;
			}
			delete [] result;
		}
		myTree.Close();
		dbFile.Close();
	}

	return 0;
}


// Hilbert curve functions-----------------------------------------------------
void Pad (char *inString) {
	for (int i = 0; inString[i] == ' ' && inString[i] != 0; i++) {
		inString[i] = '0';
	}
}

void ConvertToString (char *output, int xCoord, int yCoord) {
	bitmask_t input[2];
	input[0] = xCoord;
	input[1] = yCoord;
	bitmask_t hilbert_result = hilbert_c2i(2, sizeof(int) * 8, input); 
	sprintf (output, "%21llu", hilbert_result);
	Pad (output);
}

void ConvertToCoords (char *input, int &xCoord, int &yCoord) {
	bitmask_t otherWay;
	bitmask_t output[2];
	sscanf (input, "%llu", &otherWay);
	hilbert_i2c(2, sizeof(int) * 8, otherWay, output);
	xCoord = output[0];
	yCoord = output[1];
}

// returns a zero if we are past the box
int GetBounds (char *lowPoint, char *highPoint, int xlow, int ylow, int xhigh, int yhigh) {
	unsigned low[2];
	unsigned high[2];

	low[0] = xlow;
	low[1] = ylow;
	high[0] = xhigh;
	high[1] = yhigh;
	hilbert_box_pt (2, sizeof (int), 8 * sizeof (int), 1, low, high);
	ConvertToString (lowPoint, low[0], low[1]);

	low[0] = xlow;
	low[1] = ylow;
	high[0] = xhigh;
	high[1] = yhigh;
	hilbert_box_pt (2, sizeof (int), 8 * sizeof (int), 0, low, high);
	ConvertToString (highPoint, low[0], low[1]);
}














